/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#ifndef mozilla_css_AnimationCommon_h#define mozilla_css_AnimationCommon_h#include<algorithm> // For <std::stable_sort>#include"mozilla/AnimationCollection.h"#include"mozilla/AnimationComparator.h"#include"mozilla/EventDispatcher.h"#include"mozilla/LinkedList.h"#include"mozilla/MemoryReporting.h"#include"mozilla/dom/Animation.h"#include"mozilla/AnimationTarget.h"#include"mozilla/Attributes.h" // For MOZ_NON_OWNING_REF#include"mozilla/Assertions.h"#include"mozilla/TimingParams.h"#include"nsContentUtils.h"#include"nsCSSPseudoElements.h"#include"nsCycleCollectionParticipant.h"classnsIFrame;classnsPresContext;namespacemozilla{enumclassCSSPseudoElementType:uint8_t;namespacedom{classElement;}template<classAnimationType>classCommonAnimationManager{public:explicitCommonAnimationManager(nsPresContext*aPresContext):mPresContext(aPresContext){}// NOTE: This can return null after Disconnect().nsPresContext*PresContext()const{returnmPresContext;}/** * Notify the manager that the pres context is going away. */voidDisconnect(){// Content nodes might outlive the transition or animation manager.RemoveAllElementCollections();mPresContext=nullptr;}/** * Stop animations on the element. This method takes the real element * rather than the element for the generated content for animations on * ::before and ::after. */voidStopAnimationsForElement(dom::Element*aElement,CSSPseudoElementTypeaPseudoType){MOZ_ASSERT(aElement);AnimationCollection<AnimationType>*collection=AnimationCollection<AnimationType>::GetAnimationCollection(aElement,aPseudoType);if(!collection){return;}nsAutoAnimationMutationBatchmb(aElement->OwnerDoc());collection->Destroy();}protected:virtual~CommonAnimationManager(){MOZ_ASSERT(!mPresContext,"Disconnect should have been called");}voidAddElementCollection(AnimationCollection<AnimationType>*aCollection){mElementCollections.insertBack(aCollection);}voidRemoveAllElementCollections(){while(AnimationCollection<AnimationType>*head=mElementCollections.getFirst()){head->Destroy();// Note: this removes 'head' from mElementCollections.}}LinkedList<AnimationCollection<AnimationType>>mElementCollections;nsPresContext*mPresContext;// weak (non-null from ctor to Disconnect)};/** * Utility class for referencing the element that created a CSS animation or * transition. It is non-owning (i.e. it uses a raw pointer) since it is only * expected to be set by the owned animation while it actually being managed * by the owning element. * * This class also abstracts the comparison of an element/pseudo-class pair * for the sake of composite ordering since this logic is common to both CSS * animations and transitions. * * (We call this OwningElementRef instead of just OwningElement so that we can * call the getter on CSSAnimation/CSSTransition OwningElement() without * clashing with this object's contructor.) */classOwningElementReffinal{public:OwningElementRef()=default;explicitOwningElementRef(constNonOwningAnimationTarget&aTarget):mTarget(aTarget){}OwningElementRef(dom::Element&aElement,CSSPseudoElementTypeaPseudoType):mTarget(&aElement,aPseudoType){}boolEquals(constOwningElementRef&aOther)const{returnmTarget==aOther.mTarget;}boolLessThan(constOwningElementRef&aOther)const{MOZ_ASSERT(mTarget.mElement&&aOther.mTarget.mElement,"Elements to compare should not be null");if(mTarget.mElement!=aOther.mTarget.mElement){returnnsContentUtils::PositionIsBefore(mTarget.mElement,aOther.mTarget.mElement);}returnmTarget.mPseudoType==CSSPseudoElementType::NotPseudo||(mTarget.mPseudoType==CSSPseudoElementType::before&&aOther.mTarget.mPseudoType==CSSPseudoElementType::after);}boolIsSet()const{return!!mTarget.mElement;}voidGetElement(dom::Element*&aElement,CSSPseudoElementType&aPseudoType)const{aElement=mTarget.mElement;aPseudoType=mTarget.mPseudoType;}private:NonOwningAnimationTargetmTarget;};template<classEventInfo>classDelayedEventDispatcher{public:DelayedEventDispatcher():mIsSorted(true){}voidQueueEvent(EventInfo&&aEventInfo){mPendingEvents.AppendElement(Forward<EventInfo>(aEventInfo));mIsSorted=false;}// This is exposed as a separate method so that when we are dispatching// *both* transition events and animation events we can sort both lists// once using the current state of the document before beginning any// dispatch.voidSortEvents(){if(mIsSorted){return;}// FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is// fixed.std::stable_sort(mPendingEvents.begin(),mPendingEvents.end(),EventInfoLessThan());mIsSorted=true;}// Takes a reference to the owning manager's pres context so it can// detect if the pres context is destroyed while dispatching one of// the events.//// This will call SortEvents automatically if it has not already been// called.voidDispatchEvents(nsPresContext*const&aPresContext){if(!aPresContext||mPendingEvents.IsEmpty()){return;}SortEvents();EventArrayevents;mPendingEvents.SwapElements(events);// mIsSorted will be set to true by SortEvents above, and we leave it// that way since mPendingEvents is now emptyfor(EventInfo&info:events){EventDispatcher::Dispatch(info.mElement,aPresContext,&info.mEvent);if(!aPresContext){break;}}}voidClearEventQueue(){mPendingEvents.Clear();mIsSorted=true;}boolHasQueuedEvents()const{return!mPendingEvents.IsEmpty();}// Methods for supporting cycle-collectionvoidTraverse(nsCycleCollectionTraversalCallback*aCallback,constchar*aName){for(EventInfo&info:mPendingEvents){ImplCycleCollectionTraverse(*aCallback,info.mElement,aName);ImplCycleCollectionTraverse(*aCallback,info.mAnimation,aName);}}voidUnlink(){ClearEventQueue();}protected:classEventInfoLessThan{public:booloperator()(constEventInfo&a,constEventInfo&b)const{if(a.mTimeStamp!=b.mTimeStamp){// Null timestamps sort firstif(a.mTimeStamp.IsNull()||b.mTimeStamp.IsNull()){returna.mTimeStamp.IsNull();}else{returna.mTimeStamp<b.mTimeStamp;}}AnimationPtrComparator<RefPtr<dom::Animation>>comparator;returncomparator.LessThan(a.mAnimation,b.mAnimation);}};typedefnsTArray<EventInfo>EventArray;EventArraymPendingEvents;boolmIsSorted;};template<classEventInfo>inlinevoidImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>&aField){aField.Unlink();}template<classEventInfo>inlinevoidImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&aCallback,DelayedEventDispatcher<EventInfo>&aField,constchar*aName,uint32_taFlags=0){aField.Traverse(&aCallback,aName);}// Return the TransitionPhase or AnimationPhase to use when the animation// doesn't have a target effect.template<typenamePhaseType>PhaseTypeGetAnimationPhaseWithoutEffect(constdom::Animation&aAnimation){MOZ_ASSERT(!aAnimation.GetEffect(),"Should only be called when we do not have an effect");Nullable<TimeDuration>currentTime=aAnimation.GetCurrentTime();if(currentTime.IsNull()){returnPhaseType::Idle;}// If we don't have a target effect, the duration will be zero so the phase is// 'before' if the current time is less than zero.returncurrentTime.Value()<TimeDuration()?PhaseType::Before:PhaseType::After;};inlineTimingParamsTimingParamsFromCSSParams(floataDuration,floataDelay,floataIterationCount,dom::PlaybackDirectionaDirection,dom::FillModeaFillMode){MOZ_ASSERT(aIterationCount>=0.0&&!IsNaN(aIterationCount),"aIterations should be nonnegative & finite, as ensured by ""CSSParser");returnTimingParams{aDuration,aDelay,aIterationCount,aDirection,aFillMode};}}// namespace mozilla#endif /* !defined(mozilla_css_AnimationCommon_h) */